#include "utilc_def.h"
#include "utilc_dlinked_list.h"


util_dlinked_list_node_t* util_dlinked_list_node_create(void *value, DESTORY_FUN destory_fun) {
    SAFE_MALLOC(util_dlinked_list_node_t, ret);
    ret->value = value;
    ret->destory_fun = destory_fun;
    return ret;
}

util_dlinked_list_node_t* util_dlinked_list_node_ref(util_dlinked_list_node_t* nod){
    nod->ref++;
    return nod;
}

void util_dlinked_list_node_unref(util_dlinked_list_node_t* nod){
    if(!nod->ref)
        nod->destory_fun(nod);
    else
        nod->ref--;
}

util_dlinked_list_t* util_dlinked_list_create(){
    SAFE_MALLOC(util_dlinked_list_t, ret);
    return ret;
}

void util_dlinked_list_clear(util_dlinked_list_t *lst){
    util_dlinked_list_node_t *tmp = NULL;
    if(lst->size==0)
        return;
    tmp = lst->head;
    while(tmp)
        util_dlinked_list_node_unref(tmp);
    lst->head = NULL;
    lst->tail = NULL;
    lst->size = 0;
}

void util_dlinked_list_destory(util_dlinked_list_t *lst){
    util_dlinked_list_clear(lst);
    free(lst);
};

void util_dlinked_list_ref(util_dlinked_list_t *lst){
    lst->ref++;
}

void util_dlinked_list_unref(util_dlinked_list_t *lst){
    if(lst->ref == 0)
        util_dlinked_list_destory(lst);
    else
        lst->ref--;
}

void util_dlinked_list_push_back(util_dlinked_list_t *lst, util_dlinked_list_node_t *val){
    util_dlinked_list_node_ref(val);
    if(lst->size == 0) {
        lst->head = val;
        lst->tail = val;
        lst->size = 1;
        if(lst->ring) {
            val->prev = val->next = val;
        }
        return;
    }
    lst->tail->next = val;
    val->prev = lst->tail;
    lst->tail = val;
    lst->size++;
    if(lst->ring) {
        val->next = lst->head;
        lst->head->prev = val;
    }
}

void util_dlinked_list_push_front(util_dlinked_list_t *lst, util_dlinked_list_node_t *val){
    util_dlinked_list_node_ref(val);
    if(lst->size == 0) {
        lst->head = val;
        lst->tail = val;
        lst->size = 1;
        if(lst->ring) {
            val->prev = val->next = val;
        }
        return;
    }
    val->next = lst->head;
    lst->head->prev = val;
    lst->head = val;
    lst->size++;
    if(lst->ring) {
        lst->tail->next = val;
        val->prev = lst->tail;
    }
}

util_dlinked_list_node_t* util_dlinked_list_front(util_dlinked_list_t *lst){
    return lst->head;
}

util_dlinked_list_node_t* util_dlinked_list_back(util_dlinked_list_t *lst){
    return lst->tail;
}

void util_dlinked_list_pop_front(util_dlinked_list_t *lst){
    util_dlinked_list_node_t *tmp = lst->head;
    if(lst->size == 0) return;
    if(lst->size == 1){
        lst->head = lst->tail = NULL;
    } else {
        lst->head = lst->head->next;
        lst->head->prev = lst->tail;
        if(lst->ring) {
            lst->tail->next = lst->head;
        }
    }
    util_dlinked_list_node_unref(tmp);
    
    lst->size--;
}

void util_dlinked_list_pop_back(util_dlinked_list_t *lst){
    util_dlinked_list_node_t *tmp = lst->tail;
    if(lst->size == 0) return;
    if(lst->size == 1){
        lst->head = lst->tail = NULL;
    } else {
        lst->tail = lst->tail->prev;
        if(lst->tail)
            lst->tail->next = lst->head;
        if(lst->ring) {
            lst->head->prev = lst->tail;
        }
    }
    util_dlinked_list_node_unref(tmp);

    lst->size--;
}

void util_dlinked_list_insert(util_dlinked_list_t *lst, util_dlinked_list_node_t *pos, util_dlinked_list_node_t *val){
    util_dlinked_list_node_ref(val);
    val->prev = pos;
    val->next = pos->next;
    pos->next = val;
    val->next->prev = val;
    lst->size++;
    if(lst->tail == pos) {
        lst->tail = val;
    }
}

void util_dlinked_list_remove(util_dlinked_list_t *lst, util_dlinked_list_node_t *val){
    util_dlinked_list_node_t *tmp = lst->head;
    if(lst->size==0) return;
    if(lst->size==1 && tmp == val) {
        lst->head = NULL;
        lst->tail = NULL;
        lst->size = 0;
        util_dlinked_list_node_unref(val);
        return;
    }
    if(tmp == val) {
        lst->head = lst->head->next;
        lst->size--;
        util_dlinked_list_node_unref(val);
        return;
    }
    if(val->prev)
        val->prev->next = val->next;
    if(val->next)
        val->next->prev = val->prev;
    if(lst->head == val) {
        lst->head = val->next;
    }
    if(lst->tail == val) {
        lst->tail = val->prev;
    }
    lst->size--;
    util_dlinked_list_node_unref(val);
}

uint32_t util_dlinked_list_size(util_dlinked_list_t *lst) {
    return lst->size;
}

void dll_add_front(struct util_dll *d, struct util_dll *phead)
{
	if (d->prev)
		return;

	/* our next guy is current first guy */
	d->next = phead->next;
	/* if there is a next guy, set his prev ptr to our next ptr */
	if (d->next)
		d->next->prev = d;
	/* our prev ptr is first ptr */
	d->prev = phead;
	/* set the first guy to be us */
	phead->next = d;
}

/* situation is:
 *
 *  HEAD: struct lws_dll * = &entry1
 *
 *  Entry 1: struct lws_dll  .pprev = &HEAD , .next = Entry 2
 *  Entry 2: struct lws_dll  .pprev = &entry1 , .next = &entry2
 *  Entry 3: struct lws_dll  .pprev = &entry2 , .next = NULL
 *
 *  Delete Entry1:
 *
 *   - HEAD = &entry2
 *   - Entry2: .pprev = &HEAD, .next = &entry3
 *   - Entry3: .pprev = &entry2, .next = NULL
 *
 *  Delete Entry2:
 *
 *   - HEAD = &entry1
 *   - Entry1: .pprev = &HEAD, .next = &entry3
 *   - Entry3: .pprev = &entry1, .next = NULL
 *
 *  Delete Entry3:
 *
 *   - HEAD = &entry1
 *   - Entry1: .pprev = &HEAD, .next = &entry2
 *   - Entry2: .pprev = &entry1, .next = NULL
 *
 */

void dll_remove(struct util_dll *d)
{
	if (!d->prev) /* ie, not part of the list */
		return;

	/*
	 *  remove us
	 *
	 *  USp <-> us <-> USn  -->  USp <-> USn
	 */

	/* if we have a next guy, set his prev to our prev */
	if (d->next)
		d->next->prev = d->prev;

	/* set our prev guy to our next guy instead of us */
	if (d->prev)
		d->prev->next = d->next;

	/* we're out of the list, we should not point anywhere any more */
	d->prev = NULL;
	d->next = NULL;
}